// 创建元素并初始化属性
/**
 * @author 古宽宏
 * @param type 元素类型 默认值div
 * @param {*} props props对象字段
 * @see target 将生成的dom节点插入到目标dom节点后面
 * @see className css样式名 字符串
 * @see attribute 设置元素的属性
 * @see nodeProps 设置dom属性
 * @see listeners 设置dom的事件
 * @see style 元素行内样式 可以是字符串或者对象 案例 style="width:100px;text-align: center;"  style={width:'100px',textAlign: 'center'}
 * @see innerText 元素内部文本
 * @see child 插入到此元素的dom节点
 * @see childs 插入到此元素的dom数组
 */
function gkhCreate(type: string | undefined = 'div', props: {
    className?: string | null;
    style?: string | any;
    attribute?: any;
    child?: HTMLElement;
    target?: HTMLElement;
    innerText?: string | number;
    childs?: HTMLElement[];
    nodeProps?: any;
    listeners?: any;
} | undefined = {}) {
    if (!type) {
        type = 'div';
    }
    if (!props) {
        props = {};
    }
    const { className, style, attribute, child, target, innerText, childs, nodeProps, listeners } = props;
    const node: any = document.createElement(type);
    if (style instanceof Object) {
        for (var key in style) {
            node.style[key] = style[key];
        }
    } else if (style) {
        node.style = style;
    }
    if (attribute instanceof Object) {
        for (var key in attribute) {
            node.setAttribute(key, attribute[key]);
        }
    }
    if (nodeProps instanceof Object) {
        for (var key in nodeProps) {
            node[nodeProps] = nodeProps[key];
        }
    }
    if (listeners instanceof Object) {
        for (var key in listeners) {
            node.addEventListener(key, listeners[key], false);
        }
    }
    if (className) {
        node.setAttribute('class', className);
    }
    if (target instanceof Node) {
        target.appendChild(node);
    }
    if (innerText) {
        node.innerText = innerText;
    }
    if (child instanceof Node) {
        node.appendChild(child);
    }
    if (childs instanceof Array) {
        for (let i = 0; i < childs.length; i += 1) {
            node.appendChild(childs[i]);
        }
    }
    return node;
}

interface RecordOperationType {
    new(size?: number): RecordOperationType;
    hasNext: boolean;
    hasBack: boolean;
    pushRecord: (value: any) => void;
    push: (value: any) => void;
    next: () => any;
    back: () => any;
}
/**
 * @author 古宽宏
 * @param {*} size
 *  用于记录处理步骤  可以用于需要回退的操作
 */
function RecordOperationClass(this: RecordOperationType, size = 10): RecordOperationType {
    // 记录表格的变化用于返回到以前状态
    const records: any[] = [];
    // 记录深度
    const recordSize = size;
    // 记录当前显示下标
    let recordIndex = -1;
    this.hasNext = false;
    this.hasBack = false;

    // 插入记录
    const pushRecord = (value: any) => {
        if (value) {
            if (records.length < recordSize) {
                if (recordIndex !== records.length - 1) {
                    const len = records.length - recordIndex - 1;
                    records.splice(-len, len, value);
                } else {
                    records.push(value);
                }
            } else {
                records.shift();
                records.push(value);
            }
            recordIndex = records.length - 1;
            this.hasBack = records.length - 1 > 0;
            this.hasNext = false;
        }
    };
    this.pushRecord = pushRecord;
    this.push = pushRecord;
    // 向前获取记录
    this.next = function next() {
        if (recordIndex + 1 < records.length) {
            recordIndex += 1;
            this.hasNext = recordIndex + 1 < records.length;
            this.hasBack = true;
            return records[recordIndex];
        }
        return null;
    };
    // 向后获取记录
    this.back = function back() {
        if (recordIndex - 1 >= 0) {
            recordIndex -= 1;
            this.hasBack = recordIndex - 1 >= 0;
            this.hasNext = true;
            return records[recordIndex];
        }
        return null;
    };
    return this;
}
const RecordOperation: RecordOperationType = RecordOperationClass as unknown as RecordOperationType;

/**
 * 将rgb字符串转为16进制  rgb(0,0,0) -> #000000
 * @param {*} rgb 
 * @returns 
 */
const rgbToHex = (rgb: string) => {
    if (rgb && rgb.indexOf('rgb') == 0) {
        const strs: any[] = rgb.substring(rgb.indexOf('(') + 1, rgb.length - 1).split(',');
        const a = strs[0] > 15 ? parseInt(strs[0]).toString(16) : '0'.concat(parseInt(strs[0]).toString(16));
        const b = strs[1] > 15 ? parseInt(strs[1]).toString(16) : '0'.concat(parseInt(strs[1]).toString(16));
        const c = strs[2] > 15 ? parseInt(strs[2]).toString(16) : '0'.concat(parseInt(strs[2]).toString(16));
        return "#".concat(a).concat(b).concat(c);
    } else {
        return rgb;
    }
}

/**
 * 删除数组中的某一项或某一类数据
 * @param {*} arrs 原始数组
 * @param {*} index 删除的下标
 * @param {*} item 删除的值 可以是对象或基础类型，只要是数组当中有的
 * @param {*} key 当传入的是对象时，需要指定比较的字段 相同则删除
 */
const removeItemInArray = (arrs: Array<any>, index: any, item: any, key?: string) => {
    let size = arrs.length - 1;
    let n = index;
    if ('number' == typeof index) {
        if (index < size - 1) {
            for (let i = index; i < size; i++) {
                arrs[i] = arrs[i + 1];
            }
        }
        arrs.pop();
    } else if (item instanceof Object && key) {
        let hasNext = false;
        n = size;
        for (let i = 0; i < size; i++) {
            if (!hasNext && arrs[i] instanceof Object && item[key] == arrs[i][key]) {
                hasNext = true;
                n = i;
            }
            if (hasNext) {
                arrs[i] = arrs[i + 1];
            }
        }
        arrs.pop();
    } else if (item) {
        let hasNext = false;
        n = size;
        for (let i = 0; i < size; i++) {
            if (!hasNext && item == arrs[i]) {
                hasNext = true;
                n = i;
            }
            if (hasNext) {
                arrs[i] = arrs[i + 1];
            }
        }
        arrs.pop();
    }
    return n;
}

/**
 * 删除数组中的某一些数据
 * @param array 原始数组
 * @param items 需要删除的数组
 * @param keyMap 如果是对象数组 比较字段的字段名
 * @param dealFun 删除的时候处理函数
 * @returns 
 */
const removeItemsInArray = (array: Array<any>, items: Array<any>, keyMap?: string, dealFun?: (item: any) => void): Array<any> => {
    let temp;
    let hasNext: boolean = true;
    let n = 0;
    const newArr: Array<any> = new Array(array.length - items.length);
    if (keyMap) {
        for (let i = 0; i < array.length; i++) {
            temp = array[i][keyMap];
            hasNext = true;
            for (let j = 0; j < items.length; j++) {
                if (temp == items[j][keyMap]) {
                    hasNext = false;
                    if (dealFun) dealFun(array[i]);
                }
            }
            if (hasNext) {
                newArr[n++] = array[i];
            }
        }
    } else {
        for (let i = 0; i < array.length; i++) {
            temp = array[i];
            hasNext = true;
            for (let j = 0; j < items.length; j++) {
                if (temp == items[j]) {
                    hasNext = false;
                    if (dealFun) dealFun(array[i]);
                }
            }
            if (hasNext) {
                newArr[n++] = temp;
            }
        }
    }
    return newArr;
}

/**
 * 插入元素到数组中的某个位置
 * @param array 
 * @param insertItem 
 * @param index 
 * @returns 
 */
const insertItemInArray = (array: Array<any>, insertItem: any, index: number): Array<any> => {
    if (index < 0 || index >= array.length) {
        array.push(insertItem);
        return array;
    }
    const newArr: Array<any> = new Array(array.length + 1);
    let j = 0;
    for (let i = 0; i < array.length; i++) {
        if (i == index) {
            newArr[j++] = insertItem;
        }
        newArr[j++] = array[i];
    }
    return newArr;
}

/**
 * 创建表单
 * @param {*} data 数组 [{name: string; value?: string; placeholder?: string}]
 * @param {*} onChange 
 * @returns 
 */
interface FormDataType {
    name: string;
    label: string;
    value?: string;
    placeholder?: string;
}
const createForm = (data: FormDataType[], onChange: (event: InputEvent) => void) => {
    if (data instanceof Array) {
        return gkhCreate('form', {
            className: 'gkh-form',
            childs: data.map(item => gkhCreate('div', {
                childs: [
                    gkhCreate('label', { innerText: item.label }),
                    gkhCreate('input', { attribute: { value: item.value ? item.value : '', placeholder: item.placeholder, name: item.name }, listeners: { change: onChange } }),
                ]
            }))
        })
    }
    return undefined;
}

/**
 * 样式类名处理函数
 * @param {*} className 原始类名
 * @param {*} name 需要增加或删除的类名
 * @param {*} type 'add' | 'del'  新增还是删除
 */
const classNameDeal = (className: string, name: string, type: 'add' | 'del' | undefined = 'add') => {
    const index = className.indexOf(name);
    if (type == 'add') {
        if (index < 0) {
            if (className.length > 0) {
                className += " ".concat(name);
            } else {
                className += name;
            }
        }
    } else if (index > -1) {
        className = className.substring(0, index).concat(className.substring(index + name.length));
        if (className.charAt(0) === ' ') {
            className = className.substring(1);
        }
        if (className.charAt(className.length - 1) === ' ') {
            className = className.substring(0, className.length - 1);
        }
    }
    return className;
}

/**
 * 处理html元素的类名
 * @param node HTML节点
 * @param name 类名
 * @param add 新增还是删除  默认删除 false
 */
const dealNodeClass = (node: HTMLElement, name: string, add?: boolean) => {
    let className = node.getAttribute('class') || '';
    const index = className.indexOf(name);
    if (add) {
        if (index < 0) {
            if (className.length > 0) {
                className += " ".concat(name);
            } else {
                className += name;
            }
        }
    } else if (index > -1) {
        className = className.substring(0, index).concat(className.substring(index + name.length));
        if (className.charAt(0) === ' ') {
            className = className.substring(1);
        }
        if (className.charAt(className.length - 1) === ' ') {
            className = className.substring(0, className.length - 1);
        }
    }
    node.setAttribute('class', className)
}

/**
 * 对象 混入
 * @param target 混入目标对象
 * @param source 需要混入的对象
 */
const mixins = (target: any, source: any) => {
    if (target instanceof Object && source instanceof Object) {
        Object.getOwnPropertyNames(source).forEach(name => {
            if (!target.hasOwnProperty(name)) {
                target[name] = source[name]
            }
        })
    }
}

const getNodeInfo = (ele: HTMLElement): {
    x: number;
    left: number;
    right: number;
    y: number;
    top: number;
    bottom: number;
    width: number;
    height: number;
} => {
    const { screenX, screenY } = window;
    const { x, y, top, left, bottom, right, width, height } = ele.getBoundingClientRect();
    return {
        x: x + screenX,
        left: left + screenX,
        right: right + screenX,
        y: y + screenY,
        top: top + screenY,
        bottom: bottom + screenY,
        width,
        height,
    }
}

// 防抖装饰器 一种是一定时间范围内只执行第一次；第二种是一定时间范围内只执行最后一次
function debounceFirst(fun: (...args: any[]) => any, time = 0) {
    var preTime = 0;
    var temp = 0;
    return function (this: any, ...args: any[]) {
        temp = Date.now();
        if (temp - preTime > time) {
            preTime = temp;
            return fun.apply(this, args);
        }
    }
}
/**
 * 防抖修饰器 后执行
 * @param fun 
 * @param time 
 * @returns 
 */
function debounceLast(fun: (...args: any[]) => any, time = 0) {
    var args: any;
    var preTime = 0;
    var temp = 0;
    var me: any;
    return function (this: any) {
        args = arguments;
        me = this;
        temp = Date.now();
        if (temp - preTime > time) {
            preTime = temp;
            setTimeout(function () {
                fun.apply(me, args);
            }, time)
        }
    }
}

/**
 * 节流修饰器   一定时间内只能执行1次，第一次和最后一次一定会被执行
 * @param fun 
 * @param time 
 * @returns 
 */
function throttle(fun: (...args: any[]) => any, time = 0) {
    if (time == 0) {
        return fun;
    } else {
        var temp = false;
        var me: any;
        var args: any;
        var hasMore = false;
        var result: any;
        var timer: any;
        return function (this: any) {
            args = arguments;
            me = this;
            if (temp) {
                hasMore = true;
                return result;
            }
            hasMore = false;
            temp = true;
            result = fun.apply(me, args);
            timer = setTimeout(function () {
                clearTimeout(timer);
                temp = false;
                if (hasMore) {
                    result = fun.apply(me, args);
                }
            }, time);
            return result;
        }
    }
}

const dom = gkhCreate;
export {
    dom,        // 创建元素并初始化属性
    gkhCreate, // 创建元素并初始化属性
    RecordOperation, // 用于记录处理步骤  可以用于需要回退的操作
    RecordOperationType,
    rgbToHex, // 将rgb字符串转为16进制  rgb(0,0,0) -> #000000
    createForm, // 创建form表单
    removeItemInArray, // 删除数组中的某一项或某一类数据
    removeItemsInArray, // 删除数组中的一些数据
    insertItemInArray,  // 插入一项数据到数组中的某一个位置
    classNameDeal,  // 样式类名处理函数
    dealNodeClass,  // 处理HTML节点类名
    mixins,         // 混入
    getNodeInfo,    // 获取元素位置信息
    debounceFirst,  // 防抖修饰器 先执行 一定时间内只执行第一次
    debounceLast,   // 防抖修饰器 后置行 一定时间内只执行最后一次
    throttle,       // 节流修饰器 一定时间只执行一次，第一次和最后一次一定会执行
};
